#include "stdafx.h"
#include "RawYUVFrameQueue.h"

RawYUVFrameQueue::RawYUVFrameQueue(void)
: m_bClosed(TRUE)
, m_bGetThreadStopped(TRUE)
, m_bGetFrame(TRUE)
, m_nCapacity(FRAME_QUEUE_DEFAULT_CAPACITY)
, m_nDataBytesInQueue(0)
, m_bDataEvent(NULL)
, m_pFrameBuffer(NULL)
, m_FrameBuffer("RawYUV mem pool")
{
}

RawYUVFrameQueue::~RawYUVFrameQueue(void)
{
	ASSERT(m_bClosed);
	Close( NULL,NULL);
}

RawYUVFrameQueue::FRAME_DATA* RawYUVFrameQueue::AllocFrameDataBuffer(int nBufferSize)
{
	int nAllocSize = sizeof(FRAME_DATA) + nBufferSize;
	FRAME_DATA* PBuffer = m_FrameBuffer.Allocate(nAllocSize);
	if (PBuffer)
	{
		PBuffer->bKeyFrame = FALSE;
		PBuffer->nCapacity = nBufferSize;
		PBuffer->nDispHeight = 0;
		PBuffer->nDispWidth = 0;
		PBuffer->nDataSize = 0;
		PBuffer->nFrameCount = 0;
		PBuffer->timeStamp = 0;
		m_nDataBytesInQueue += PBuffer->nCapacity;

	}
	return PBuffer;
}

void RawYUVFrameQueue::FreeFrameDataBuffer(FRAME_DATA* pFrameData)
{
	m_nDataBytesInQueue -= pFrameData ->nCapacity;
	m_FrameBuffer.Free(pFrameData);
}

void RawYUVFrameQueue::Initialize(int nCapacity)
{
	ASSERT(m_bClosed);
	ASSERT(m_pFrameBuffer == NULL);
	if (!m_bClosed)
	{
		return;
	}
	ASSERT(m_nDataBytesInQueue == 0);
	m_FrameBuffer.Initialize(256*1024 + sizeof(FRAME_DATA),30);
	m_nCapacity = nCapacity;
	m_nDataBytesInQueue = 0;

	m_bDataEvent = CreateEvent(NULL,TRUE,FALSE,NULL);
	m_bStopDequeueEvent = CreateEvent(NULL,TRUE,FALSE,NULL);

	m_bGetFrame = TRUE;
	m_bClosed = FALSE;
}


void RawYUVFrameQueue:: PushFrameData(FRAME_DATA* pFrameData)
{
	m_DataQueue.push_back(pFrameData);
	if (m_DataQueue.size()== 1)
	{
		SetEvent(m_bDataEvent);
	}
}

void RawYUVFrameQueue::PopFrameData()
{
	FRAME_DATA* pFrameData = m_DataQueue.front();
	m_DataQueue.pop_front();
	FreeFrameDataBuffer(pFrameData);
	if (m_DataQueue.empty())
	{
		ResetEvent(m_bDataEvent);
	}

}

BOOL RawYUVFrameQueue::PushRawFrameData(const unsigned char* pData,int nSize, int DispHeight,int DisWidth,BOOL bKeyFrame, __int64 timeStamp)
{
	THREAD_SAFE_ACCESS(m_DataQueueCS);
	if (m_bClosed)
	{
		return FALSE;
	}

	while (m_nDataBytesInQueue > m_nCapacity)
	{
		PopFrameData(); //DISCARD the front data
	}
	FRAME_DATA* pFrameData = AllocFrameDataBuffer(nSize);
	memcpy(pFrameData->pData,pData,nSize);
	pFrameData->bKeyFrame = bKeyFrame;
	pFrameData->timeStamp = timeStamp;
	pFrameData->nDispHeight = DispHeight;
	pFrameData->nDispWidth = DisWidth;
	pFrameData->nDataSize = nSize;
	PushFrameData(pFrameData);
	
	return  TRUE;
}

QUEUE_ACTION RawYUVFrameQueue::GetOneFrameData(GET_FRAME_PROC pGetFrameProc,DWORD dwWaitTime /* = INFINITE */,void* pUserData)
{
	const DWORD WAIT_DATA_READY_EVENT = WAIT_OBJECT_0;
	const DWORD WAIT_STOP_DEQUEUE_EVENT = WAIT_OBJECT_0 + 1;

	HANDLE hEvents[] = {m_bDataEvent,m_bStopDequeueEvent};
	DWORD dwWait = WaitForMultipleObjects(2, hEvents,FALSE,dwWaitTime);

	switch(dwWait)
	{
	case WAIT_DATA_READY_EVENT:
		break;
	case WAIT_STOP_DEQUEUE_EVENT:
		m_bGetFrame =FALSE;
		return ACTION_STOP_GET_FRAME;
	case WAIT_TIMEOUT:
		return ACTION_STOP_GET_FRAME;
	default:
		return ACTION_GET_FAILED;
	}
	THREAD_SAFE_ACCESS(m_DataQueueCS);
	ASSERT(m_bGetFrame);
	if (!m_bGetFrame)
	{
		return ACTION_GET_DISABLED;
	}
	if(m_bClosed)
	{
        return ACTION_STOP_GET_FRAME;
	}

	FRAME_DATA* pFrameData = m_DataQueue.front();
	QUEUE_ACTION act = pGetFrameProc(pFrameData->pData,pFrameData->nDataSize,pFrameData->timeStamp,pUserData);
	if (act ==  ACTION_GET_NEXT_FRAME)
	{
		PopFrameData();
	}
	else if (act == ACTION_STOP_GET_FRAME)
	{
		m_bGetFrame = FALSE;

	}

	return act;
}

BOOL RawYUVFrameQueue::StartGetFrameThread(GET_FRAME_PROC pGetFrameProc,void* pUserData)
{
	if (m_bClosed)
	{
		return FALSE;
	}
	m_bGetThreadStopped = FALSE;
	ResetEvent(m_bStopDequeueEvent);

	m_pGetFrameProc = pGetFrameProc;
	m_pUserData = pUserData;
	m_bGetFrame = TRUE;
	if (m_GetFrameThread.Start(GetFrameThreadProc,this))
	{
		return TRUE;
	}
	else
	{
		return FALSE;
	}
	

}

void RawYUVFrameQueue::StopGetFrameThread()
{
	m_bGetThreadStopped = TRUE;
	SetEvent(m_bStopDequeueEvent);

	m_GetFrameThread.WaitSafeExit();
}

DWORD RawYUVFrameQueue::GetFrameThreadProc(LPVOID pParam)
{
	RawYUVFrameQueue* pThis = static_cast<RawYUVFrameQueue*> (pParam);
	pThis->GetFrameProc();
	return 0;
}


void RawYUVFrameQueue ::GetFrameProc()
{
	while(!m_bGetThreadStopped)
	{
		QUEUE_ACTION act = GetOneFrameData(m_pGetFrameProc,INFINITE,m_pUserData);
		switch(act)
		{
		case ACTION_GET_NEXT_FRAME:
			break;
		case ACTION_GET_TIME_OUT:
			break;
		default:
			return;
		}
	}
}

void RawYUVFrameQueue::Close(END_GET_PROC pEndGetProc,void* pUserData)
{
	if(m_bClosed)
	{
		return;
	}
	else
	{
		THREAD_SAFE_ACCESS(m_DataQueueCS);
		m_bClosed = TRUE;
	}
	SetEvent(m_bStopDequeueEvent);

	if (pEndGetProc != NULL)
	{
		pEndGetProc(pUserData);
	}
	if (!m_bGetThreadStopped)
	{
		m_bGetThreadStopped = TRUE;
		m_GetFrameThread.WaitSafeExit(INFINITE);
	}
	while(!m_DataQueue.empty())
	{
		FreeFrameDataBuffer(m_DataQueue.front());
		m_DataQueue.pop_front();
	}
	CloseHandle(m_bDataEvent);
	CloseHandle(m_bStopDequeueEvent);
	m_bDataEvent = NULL;
	m_bStopDequeueEvent = NULL;
}